#include <inttypes.h>

#ifndef __XEN_TOOLS__
#define __XEN_TOOLS__ 1
#endif

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <stddef.h>
#include <stdint.h>
#include <xen/xen.h>
#include <xen/domctl.h>
#include <xen/physdev.h>
#include <xen/sysctl.h>
#include <xen/version.h>
#include <xen/event_channel.h>
#include <xen/sched.h>
#include <xen/memory.h>
#include <xen/hvm/params.h>
#include <xen/xsm/acm.h>
#include <xen/xsm/acm_ops.h>
#include <xen/xsm/flask_op.h>

#include <xen/sys/privcmd.h>

#include <sys/syscall.h>
#define DECLARE_HYPERCALL privcmd_hypercall_t hypercall2 = { 0 }


#ifdef __ia64__
#define XC_PAGE_SHIFT           14
#else
#define XC_PAGE_SHIFT           12
#endif
#define PAGE_SIZE            (1UL << XC_PAGE_SHIFT)
#define PAGE_MASK            (~(PAGE_SIZE-1))

DECLARE_HYPERCALL;

/*
 * unsigned long long gettsc(void);
 * int init_sync_(void);
 * int finish_sync_(void);
 * int before_sync_(void);
 * int after_sync_(void);
 */
//global variables
int sync_on_xen = 0;
int sync_xen_priv_cmd_fd = -1;
unsigned int my_tid;
int sync_steps;

typedef struct param_t{
    unsigned int tid;
    int sync_steps;
}sync_param;

sync_param param;

unsigned gettid(void)
{
	return syscall(__NR_gettid);
}

unsigned long long gettsc(void) {    //get TSC value from EDX:EAX
    unsigned long ax, dx;
    asm volatile("rdtsc" : "=a" (ax), "=d" (dx));
    return ((unsigned long long)dx << 32) + ax;
}

int init_sync_(void)
{
//now test if we are running on top of Xen, by opening the private command
//	file in /proc file system
    int flags;
    //Add by hejian
    int ret;


	//printf("In init_sync_!\n");
    //my_tid = (unsigned int)gettid();
    sync_steps=0;
    param.tid = (unsigned int )gettid();//my_tid;
    param.sync_steps = sync_steps;

    sync_xen_priv_cmd_fd = open("/proc/xen/privcmd", O_RDWR);

    if ( sync_xen_priv_cmd_fd == -1 )
    {
        printf("Could not obtain handle on privileged command interface.\n");
        return -1;
    }

    if ( (flags = fcntl(sync_xen_priv_cmd_fd, F_GETFD)) < 0 )
    {
        printf("Could not get file handle flags.\n");
        goto error;
    }
    flags |= FD_CLOEXEC;
    if ( fcntl(sync_xen_priv_cmd_fd, F_SETFD, flags) < 0 )
    {
        printf("Could not set file handle flags.\n");
        goto error;
    }


#ifdef VERBOSE_DBG_MSG
	printf( "init_xen_env() is called! and returns successfully\n" );
#endif
	printf( "I (TID:%d) am running on Xen, start_tsc = %llu\n", (unsigned int)gettid(), gettsc() );
	sync_on_xen = 1;
    if (sync_on_xen != 0)
    {
        hypercall2.op       =   __HYPERVISOR_sched_op;
        hypercall2.arg[0]   =   (unsigned long)9;
        hypercall2.arg[1]   =   (unsigned long)&param;
        
        ret = ioctl(sync_xen_priv_cmd_fd, IOCTL_PRIVCMD_HYPERCALL, (unsigned long)&hypercall2);
        if (ret < 0)
        {
            printf( "Process TID: %u: init_sync_ is FAILED! Sync_step:%d.\n", (unsigned int)gettid(), sync_steps );
        }
    }
    return sync_xen_priv_cmd_fd;

error:
    close(sync_xen_priv_cmd_fd);
    printf( "init_sync_() is called, but file descriptor does not good!\n" );
    return -1;
}

int before_sync_( void )
{
    int ret;
    printf( "TSC:%llu, Process TID: %u: before_sync_ is called, Sync_step:%d.\n", gettsc(), (unsigned int)gettid(), sync_steps );

    param.tid = (unsigned int)gettid();//my_tid;
    param.sync_steps = sync_steps;

    if( sync_on_xen != 0 ){
        hypercall2.op     = __HYPERVISOR_sched_op;
        hypercall2.arg[0] = (unsigned long) 7;
        hypercall2.arg[1] = (unsigned long) &param;

    	ret =  ioctl( sync_xen_priv_cmd_fd,
		IOCTL_PRIVCMD_HYPERCALL, 
		(unsigned long) &hypercall2 );
	if( ret < 0 ) printf( "Process TID: %u: before_sync_ is FAILED! Sync_step:%d.\n", (unsigned int)gettid(), sync_steps );
    }
    return 0;
}

int after_sync_(void)
{
    int ret;
    printf( "TSC:%llu, Process TID: %u: after_sync_ is called. Sync_steps:%d.\n", gettsc(), (unsigned int)gettid(), sync_steps );

    param.tid = (unsigned int)gettid();//my_tid;
    param.sync_steps = sync_steps;

    if( sync_on_xen != 0 ){
        hypercall2.op     = __HYPERVISOR_sched_op;
        hypercall2.arg[0] = (unsigned long) 8;
        hypercall2.arg[1] = (unsigned long) &param;

    	ret =  ioctl( sync_xen_priv_cmd_fd,
		IOCTL_PRIVCMD_HYPERCALL, 
		(unsigned long) &hypercall2 );
	if( ret < 0 ) printf( "Process TID: %u: after_sync_ is FAILED! Sync_step:%d.\n", (unsigned int)gettid(), sync_steps );
    }
    sync_steps++;
    return 0;
}

int finish_sync_( void )
{
	int ret;
	//param.tid = my_tid;

	printf("In finish_sync_!\n");
	//my_tid = (unsigned int )gettid();
	param.tid = (unsigned int)gettid();
	param.sync_steps = sync_steps;
    if (sync_on_xen != 0)
    {
        hypercall2.op       =   __HYPERVISOR_sched_op;
        hypercall2.arg[0]   =   (unsigned long)10;
        hypercall2.arg[2]   =   (unsigned long)&param;
        
        ret = ioctl(sync_xen_priv_cmd_fd, IOCTL_PRIVCMD_HYPERCALL, (unsigned long)&hypercall2);
        if (ret < 0)
        {
            printf( "Process TID: %u: finish_sync_ is FAILED! Sync_step:%d.\n", (unsigned int)gettid(), sync_steps );
        }
    }
	printf( "I (TID:%d) finished running on Xen, finish_tsc = %llu\n", (unsigned int)gettid(), gettsc() );
    fflush( stdout );
    if( sync_xen_priv_cmd_fd != -1 )
	close( sync_xen_priv_cmd_fd );
    return 0;
}

